package com.ipay.redis;

import java.io.*;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import redis.clients.jedis.ShardedJedis;

import com.ipay.exception.BaseException;
import com.ipay.lang.RunnableWithReturn;

@Component
public class JedisManager {
	public static final String OP_QUERY = "jedis.query";
	public static final String OP_EXCUTE = "jedis.transaction";
	public static final String OP_ADD = "jedis.add";
	public static final String OP_DELETE = "jedis.delete";
	public static final String OP_GET = "jedis.connection";
	public static final String OP_CLOSE = "jedis.close";
	
	private static Log log = LogFactory.getLog(JedisManager.class);
	
	@Autowired private JedisConnectionPool pool;
	
	private static final ThreadLocal<ShardedJedis> jedisInstances = new ThreadLocal<ShardedJedis>();
	
	private ShardedJedis getShardedJedis(){
		ShardedJedis jedis = jedisInstances.get();
		if(jedis == null){
			jedisInstances.set(jedis = pool.shardPool.getResource());
		}
		return jedis;
		
	}
	
	private void close(final ShardedJedis jedis){
		if(jedis != null){
			jedis.disconnect();
			pool.shardPool.returnResource(jedis);
			jedisInstances.remove();
		}
		
//		pool.shardPool.destroy();
	}
	
	public <R> R doInJedisContext(RunnableWithReturn<R> command) {
		JedisContext.get().jedis = this.getShardedJedis();
		try{
			return command.execute();
		} catch (Exception e) {
//			log.debug("Jedis事物处理失败：", e);
			throw new BaseException("Jedis事物处理失败:" + e.getMessage() ,e);
		}finally{
			this.close(JedisContext.get().jedis);
		}
	}
	
	void setValue(final String key, final String value){
		this.doInJedisContext(new RunnableWithReturn<Void>() {
			public Void execute() throws Exception {
				JedisContext.get().add(key, value);
				return null;
			}
		});
	}
	
	void setValue(final String key, final String value, final int expire){
		this.doInJedisContext(new RunnableWithReturn<Void>() {
			public Void execute() throws Exception {
				JedisContext.get().add(key, value, expire);
				return null;
			}
		});
	}
	
	String getValue(final String key){
		return this.doInJedisContext(new RunnableWithReturn<String>() {
			public String execute() throws Exception {
				return JedisContext.get().getString(key);
			}
		});
	}
	
	String remove(final String key){
		return this.doInJedisContext(new RunnableWithReturn<String>() {
			public String execute() throws Exception {
				return JedisContext.get().remove(key);
			}
		});
	}
	void setObj(final String key, final JedisEntity value){
		this.doInJedisContext(new RunnableWithReturn<Void>() {
			public Void execute() throws Exception {
				JedisContext.get().addObj(key, value);
				return null;
			}
		});
	}
	
	void setObj(final String key, final JedisEntity value, final int expire){
		this.doInJedisContext(new RunnableWithReturn<Void>() {
			public Void execute() throws Exception {
				JedisContext.get().addObj(key, value, expire);
				return null;
			}
		});
	}
	
	JedisEntity getObj(final String key){
		return this.doInJedisContext(new RunnableWithReturn<JedisEntity>() {
			public JedisEntity execute() throws Exception {
				return JedisContext.get().getObj(key);
			}
		});
	}
	
	void removeObj(final String key){
		this.doInJedisContext(new RunnableWithReturn<Void>() {
			public Void execute() throws Exception {
				JedisContext.get().removeObj(key);
				return null;
			}
		});
	}
	
	
	public static class JedisContext{
		private ShardedJedis jedis;
		public JedisContext(){
			
		}
		private static final ThreadLocal<JedisContext> instances = new ThreadLocal<JedisContext>();
		public static JedisContext get(){
			JedisContext context = instances.get();
			if(context == null) instances.set(context = new JedisContext());
			return context;
		}
		public ShardedJedis getJedis(){
			return this.jedis;
		}
		public JedisContext add(final String key, final String value){
			this.jedis.set(key, value);
			return this;
		}
		public JedisContext add(final String key, final String value, final int expire){
			this.jedis.setex(key, expire, value);
			return this;
		}
		public String getString(final String key){
			return this.jedis.get(key);
		}
		public String remove(final String key){
			String s = this.jedis.get(key);
			jedis.del(key);
			return s;
		}
		public JedisContext addObj(final String k, final JedisEntity obj){
			try {
				byte[] bts = this.serialize(obj);
				this.jedis.set(k.getBytes("utf-8"), bts);
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
			return this;
		}
		
		public JedisContext addObj(final String key, final JedisEntity obj, final int expire){
			try {
				byte[] bts = this.serialize(obj);
				jedis.setex(key.getBytes("utf-8"), expire, bts);
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
			return this;
		}
		
		public JedisEntity getObj(final String key){
			try {
				byte[] bts = jedis.get(key.getBytes("utf-8"));
				if (bts != null) return unserialize(bts);
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
			return null;
			
		}
		
		public void removeObj(final String key){
			jedis.del(key);
		}
		
		
		public byte[] serialize(JedisEntity obj){
			ByteArrayOutputStream bos =  null;
			ObjectOutputStream oos = null;
			byte[] bts = null;
			try {
				bos = new ByteArrayOutputStream();
				oos = new ObjectOutputStream(bos);
				oos.writeObject(obj);
				bts = bos.toByteArray();
			} catch (IOException e) {
				log.warn("Serialize JedisEntity OutputStream case error: ", e);
			}finally{
				if(oos != null)
					try {
						oos.close();
					} catch (IOException e) {
						log.warn("Serialize JedisEntity ObjectOutputStream close error: ",e);
					}
				if(bos != null)
					try {
						bos.close();
					} catch (IOException e) {
						log.warn("Serialize JedisEntity ByteArrayOutputStream close error: ",e);
					}
			}
			return bts;
		}
		
		public JedisEntity unserialize(byte[] bts){
			ByteArrayInputStream bis = null;
			ObjectInputStream ois = null;
			Object obj = null;
			try{
				bis = new ByteArrayInputStream(bts);
				ois = new ObjectInputStream(bis);
				obj = ois.readObject();
			} catch (IOException e) {
				log.warn("UnSerialize JedisEntity IOException:", e);
			} catch (ClassNotFoundException e) {
				log.warn("UnSerialize JedisEntity Class not found:", e);
			}finally{
				if(ois != null)
					try {
						ois.close();
					} catch (IOException e) {
						log.warn("UnSerialize JedisEntity ObjectInputStream close error: ",e);
					}
				if(bis != null)
					try {
						bis.close();
					} catch (IOException e) {
						log.warn("UnSerialize JedisEntity ByteArrayInputStream close error: ",e);
					}
			}
			if(obj != null && obj instanceof JedisEntity){
				return (JedisEntity) obj;
			}
			return null;
		}
		
	}
	
}
